package com.yixin.hospital.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yixin.hospital.common.ApiResult;
import com.yixin.hospital.common.BusinessException;
import com.yixin.hospital.entity.domain.*;
import com.yixin.hospital.entity.dto.req.paid.CardPayInfo;
import com.yixin.hospital.entity.dto.req.paid.DrugPayInfo;
import com.yixin.hospital.entity.dto.req.paid.PaidAddReqDto;
import com.yixin.hospital.entity.vo.CurVisitAmountVo;
import com.yixin.hospital.enums.*;
import com.yixin.hospital.mapper.PaidMapper;
import com.yixin.hospital.service.*;
import com.yixin.hospital.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.validation.Valid;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author Administrator
 * @description 针对表【paid(支付表)】的数据库操作Service
 * @createDate 2025-05-17 00:18:17
 */
@Service
@Slf4j
public class PaidServiceImpl extends ServiceImpl<PaidMapper, Paid>
    implements PaidService {

    @Autowired
    private VisitInspectionListService inspectionListService;

    @Autowired
    private VisitSubPackageDrugService subPackageDrugService;

    @Autowired
    private VisitPackageDrugService packageDrugService;

    @Autowired
    private PatientCardService patientCardService;

    @Autowired
    private VisitService visitService;
    @Autowired
    private CardService cardService;
    @Autowired
    private GuidanceService guidanceService;
    @Autowired
    private VisitSubPackageDrugService visitSubPackageDrugService;
    @Autowired
    private VisitPackageDrugService visitPackageDrugService;
    @Autowired
    private VisitInspectionListService visitInspectionListService;
    @Autowired
    private PaidService paidService;
    @Autowired
    private ReturnPaidService returnPaidService;


    @Override
    public ApiResult<CurVisitAmountVo> getCurVisitAmount(String guidanceCode, String patientId) {
        CurVisitAmountVo curVisitAmountVo = new CurVisitAmountVo();
        // 获取本次处方信息
        Visit visit = visitService.getOne(new LambdaQueryWrapper<Visit>().eq(Visit::getGuidanceCode, guidanceCode));
        // 获取分装要品未支付金额
        List<VisitSubPackageDrug> subPackagePendingList = subPackageDrugService.list(new LambdaQueryWrapper<VisitSubPackageDrug>().eq(VisitSubPackageDrug::getGuidanceCode, guidanceCode).eq(VisitSubPackageDrug::getStatus, DrugVisitStatusEnum.PENDING.getCode()));
        if (!CollectionUtils.isEmpty(subPackagePendingList)) {
            BigDecimal subPackageTotalAmount = subPackagePendingList.stream().map(VisitSubPackageDrug::getRealSalePrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            curVisitAmountVo.setSubPackageDrugAmount(subPackageTotalAmount);
            curVisitAmountVo.setSubPackagePendingList(subPackagePendingList);
            // 期限卡（每个会员只能办一张）
            PatientCard limitCard = patientCardService.getOne(new LambdaQueryWrapper<PatientCard>().eq(PatientCard::getPatientId,patientId).eq(PatientCard::getCardType, CardTypeEnum.LIMIT_DATE_CARD.getCode()).in(PatientCard::getStatus, PatientCardStatusEnum.PENDING.getCode(), PatientCardStatusEnum.VALID.getCode()));
            if (Objects.nonNull(limitCard)) {
                Card cardSetting = cardService.getById(limitCard.getCardId());
//                LocalDateTime startUsageTime = visit.getStartUsageTime();
//                LocalDateTime endUsageTime = visit.getEndUsageTime();
                // 期限卡使用日期记录   add [{"date":"20250501", "amount": 12}, {"date":"20250502", "amount": 12}]
//                List<LimitCardInfoBo> limitCardInfo = JSONArray.parseArray(limitCard.getLimitCardInfo(), LimitCardInfoBo.class);
//                Map<String, LimitCardInfoBo> limitCardInfoBoMap = limitCardInfo.stream().collect(Collectors.toMap(LimitCardInfoBo::getDate, Function.identity()));
//                for (LocalDateTime dateTime = startUsageTime; dateTime.compareTo(endUsageTime) <= 0 ; dateTime=dateTime.plusDays(1)) {
//                    String date = DateUtil.dateToString(dateTime, DateUtil.YMN);
//                    LimitCardInfoBo tmpBo = limitCardInfoBoMap.get(date);
//                    if (Objects.isNull(tmpBo) || tmpBo.getStatus() == 1) continue;    // 超过日期 或 该日期已经减免
//                    tmpBo.setStatus(1);
//                }
                // TODO 计算期限卡免费金额（每日免费金额不同，如何计算每日金额？）
                BigDecimal totalLimitCardFreeAmount = this.getCurrentDayAmount(subPackagePendingList, cardSetting, limitCard, visit);
//                limitCard.setLimitCardInfo(JSONArray.toJSONString(limitCardInfo));
//                patientCardService.updateById(limitCard);
                curVisitAmountVo.setLimitCardFreeAmount(totalLimitCardFreeAmount);
                curVisitAmountVo.setSubPackageDrugAmount(subPackageTotalAmount.subtract(totalLimitCardFreeAmount));
                // 期限卡使用天数、剩余天数
                this.setLimitCardUseInfo(limitCard, visit, curVisitAmountVo);
            }
        }
        // 获取整装药品未支付金额
        List<VisitPackageDrug> packagePendingList = packageDrugService.list(new LambdaQueryWrapper<VisitPackageDrug>().eq(VisitPackageDrug::getGuidanceCode, guidanceCode).eq(VisitPackageDrug::getStatus, DrugVisitStatusEnum.PENDING.getCode()));
        if (!CollectionUtils.isEmpty(packagePendingList)) {
            BigDecimal packageTotalAmount = packagePendingList.stream().map(VisitPackageDrug::getRealSalePrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            curVisitAmountVo.setPackageDrugAmount(packageTotalAmount);
            curVisitAmountVo.setPackagePendingList(packagePendingList);
        }
        // 获取检查项未支付金额
        List<VisitInspectionList> inspectionPendingList = inspectionListService.list(new LambdaQueryWrapper<VisitInspectionList>().eq(VisitInspectionList::getGuidanceCode, guidanceCode).eq(VisitInspectionList::getStatus, DrugVisitStatusEnum.PENDING.getCode()));
        if (!CollectionUtils.isEmpty(inspectionPendingList)) {
            BigDecimal totalAmount = BigDecimal.ZERO;
            for (VisitInspectionList visitInspectionList : inspectionPendingList) {
                totalAmount = totalAmount.add(visitInspectionList.getUnitPrice().multiply(new BigDecimal(visitInspectionList.getRealQuantity())));  // 单价 * 数量 累计金额
            }
            curVisitAmountVo.setInspectionAmount(totalAmount);
            curVisitAmountVo.setInspectionPendingList(inspectionPendingList);
        }
        // 获取办卡未支付金额
        List<PatientCard> cardList = patientCardService.list(new LambdaQueryWrapper<PatientCard>().eq(PatientCard::getPatientId,patientId).eq(PatientCard::getStatus, PatientCardStatusEnum.PENDING.getCode()));
        if (!CollectionUtils.isEmpty(cardList)) {
            BigDecimal totalAmount = BigDecimal.ZERO;
            for (PatientCard patientCard : cardList) {
                JSONObject rule = JSONObject.parseObject(patientCard.getCardRule().toString());
                BigDecimal amount = new BigDecimal(rule.getString("amount"));
                totalAmount = totalAmount.add(amount);
            }
            curVisitAmountVo.setCardAmount(totalAmount);
            curVisitAmountVo.setCardList(cardList);
        }
        Guidance guidance = guidanceService.getOne(new LambdaQueryWrapper<Guidance>().eq(Guidance::getGuidanceCode, guidanceCode));
        Integer guidanceBetweenMinute = (int) ChronoUnit.MINUTES.between(guidance.getCreatedTime(), LocalDateTime.now());
        curVisitAmountVo.setAfterGuidanceTime(DateUtil.toChineseDate(guidanceBetweenMinute));
        curVisitAmountVo.setGuidance(guidance);
        return ApiResult.success(curVisitAmountVo);
    }

    @Override
    public ApiResult<CurVisitAmountVo> add(PaidAddReqDto paidAddReqDto, String userName) {
        // TODO 校验期限卡金额
        // 校验产品金额
        CurVisitAmountVo curVisitAmountVo = this.checkPayAmount(paidAddReqDto);
        // 修改各产品支付状态, 扣减储值卡金额
        this.updateDrugStatus(curVisitAmountVo, paidAddReqDto);
        // 根据业务修改主记录状态  TODO 同时有取药和检查? 改为取药状态
        this.updateGuidanceStatus(curVisitAmountVo, userName);
        // 保存支付记录
        this.savePaidRecord(curVisitAmountVo, userName);
        // 保存欠款记录
        this.saveReturnPaidRecord(paidAddReqDto, curVisitAmountVo, userName);

        return ApiResult.success(curVisitAmountVo);
    }

    private void setLimitCardUseInfo(PatientCard limitCard, Visit visit, CurVisitAmountVo curVisitAmountVo) {
        LocalDateTime limitCardStartDate = limitCard.getLimitCardStartDate();
        LocalDateTime limitCardEndDate = limitCardStartDate.plusDays(limitCard.getLimitCardDays());
        // 发药开始日期
        LocalDateTime startUsageTime = visit.getStartUsageTime();
        // 发药结束日期
        LocalDateTime endUsageTime = startUsageTime.plusDays(visit.getPlanUsageDays());
        // 使用天数：期限卡开始日期 < 发药结束日期 ? 期限卡开始日期 至 发药结束日期 : 0
        Integer usedDays = 0;
        LocalDateTime endDate = limitCardEndDate.compareTo(endUsageTime) < 0 ? limitCardEndDate : endUsageTime;
        if (limitCardStartDate.compareTo(endUsageTime) <= 0) {
            usedDays = Math.abs(DateUtil.getDiffDays(limitCardStartDate.toLocalDate(), endDate.toLocalDate()));
        }
        curVisitAmountVo.setLimitCardUsedDays(usedDays);
        curVisitAmountVo.setLimitCardResidueDays(limitCard.getLimitCardDays() - usedDays);
    }

    private BigDecimal getCurrentDayAmount(List<VisitSubPackageDrug> subPackagePendingList, Card cardSetting, PatientCard limitCard, Visit visit) {
        // 期限卡过滤 todo 有剩余药时怎么减免？
        List<String> purposeEnumList = Arrays.asList(UsagePurposeEnum.DAILY.getDesc(), UsagePurposeEnum.DAILY_SPECIAL.getDesc());
        BigDecimal freeTotalAmount = BigDecimal.ZERO;
        LocalDateTime startUsageTime = visit.getStartUsageTime();
        LocalDateTime endUsageTime = visit.getEndUsageTime();
        LocalDateTime limitEndTime = limitCard.getLimitCardStartDate().plusDays(limitCard.getLimitCardDays());
        for (VisitSubPackageDrug visitSubPackageDrug : subPackagePendingList) {
            // 检查：是期限卡包含的药品则进行计算，否则continue
            // 1.每日金额 = 单价*每日片数
            // 2.日常： [开始时间 与（ 期限卡结束时间<处方结束时间 ）相差天数] * 每日金额
            // 3.日常特：[开始日期+天起 与（ 期限卡结束时间<处方结束时间 ）相差天数] * 每日金额
            // 4.期限卡累计减免金额：累计相加2 3
            if (!cardSetting.getDrugCode().contains(visitSubPackageDrug.getCode())) continue;   // 包含在期限卡药品
            if (!purposeEnumList.contains(visitSubPackageDrug.getUsagePurpose())) continue; // 日常与日常特
            BigDecimal dayAmount = visitSubPackageDrug.getUnitPrice().multiply(new BigDecimal(visitSubPackageDrug.getEachUsageAmount()));   // 单价 * 用量
            LocalDateTime endDate = limitEndTime.compareTo(endUsageTime) < 0 ? limitEndTime : endUsageTime; // 优惠结束日期（期限卡结束日期/开药结束日期）
            // todo 剩余药的天: 需开10片每日1片，期限卡5日到期；此时剩余3片，实开7片；
            // todo if 如果7片里 5片算期限卡，2片自费则不用动 else 如果7片里5片算自费，2片算期限卡，则diffDays = diffDays - reserveNumDays
            if (Objects.nonNull(visitSubPackageDrug.getReserveNum())) {
                int reserveNumDays = visitSubPackageDrug.getReserveNum() % visitSubPackageDrug.getEachUsageAmount() == 0 ? visitSubPackageDrug.getReserveNum() / visitSubPackageDrug.getEachUsageAmount() : visitSubPackageDrug.getReserveNum() / visitSubPackageDrug.getEachUsageAmount() + 1;
            }
            if (startUsageTime.toLocalDate().compareTo(endDate.toLocalDate()) > 0) return freeTotalAmount;
            if (visitSubPackageDrug.getUsagePurpose().equals(UsagePurposeEnum.DAILY.getDesc())) {
                int diffDays = DateUtil.getDiffDays(startUsageTime.toLocalDate(), endDate.toLocalDate());
                freeTotalAmount = freeTotalAmount.add(dayAmount.multiply(new BigDecimal(diffDays)));
            } else if (visitSubPackageDrug.getUsagePurpose().equals(UsagePurposeEnum.DAILY_SPECIAL.getDesc())) {
                int diffDays = DateUtil.getDiffDays(startUsageTime.plusDays(visitSubPackageDrug.getPlanUsageStartDays()).toLocalDate(), endDate.toLocalDate());
                freeTotalAmount = freeTotalAmount.add(dayAmount.multiply(new BigDecimal(diffDays)));
            }
        }
        return freeTotalAmount;
    }

    private void saveReturnPaidRecord(PaidAddReqDto paidAddReqDto, CurVisitAmountVo curVisitAmountVo, String userName) {
        CardPayInfo cardPayInfo = paidAddReqDto.getCardPayInfo();
        DrugPayInfo drugPayInfo = paidAddReqDto.getDrugPayInfo();
        if (Objects.nonNull(cardPayInfo.getDebtAmount()) && cardPayInfo.getDebtAmount().compareTo(BigDecimal.ZERO) != 0
                || Objects.nonNull(drugPayInfo.getDebtAmount()) && drugPayInfo.getDebtAmount().compareTo(BigDecimal.ZERO) != 0
        ) {
            // 有欠款
            ReturnPaid returnPaid = new ReturnPaid();
            returnPaid.setDeptId(curVisitAmountVo.getGuidance().getDeptId());
            returnPaid.setGuidanceCode(paidAddReqDto.getGuidanceCode());
            if (Objects.nonNull(cardPayInfo.getDebtAmount())) {
                returnPaid.setCardDebtAmount(cardPayInfo.getDebtAmount());
            }
            if (Objects.nonNull(drugPayInfo.getDebtAmount())) {
                returnPaid.setDrugDebtAmount(drugPayInfo.getDebtAmount());
            }
            returnPaid.setCreatedBy(userName);
            returnPaid.setUpdatedBy(userName);
            returnPaidService.save(returnPaid);
        }
    }

    private void savePaidRecord(CurVisitAmountVo curVisitAmountVo, String userName) {
        Paid paid = new Paid();
        paid.setGuidanceCode(curVisitAmountVo.getGuidance().getGuidanceCode());
        paid.setCardAmount(curVisitAmountVo.getCardAmount());
        paid.setSubPackageDrugAmount(curVisitAmountVo.getSubPackageDrugAmount());
        paid.setPackageDrugAmount(curVisitAmountVo.getPackageDrugAmount());
        paid.setInspectionAmount(curVisitAmountVo.getInspectionAmount());
        paid.setLimitCardFreeAmount(curVisitAmountVo.getLimitCardFreeAmount());
        // cardRecords
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getCardList())) {
            List<Long> cardIds = curVisitAmountVo.getCardList().stream().map(PatientCard::getId).collect(Collectors.toList());
            paid.setCardIds(JSONArray.toJSONString(cardIds));
        }
        // subPackageRecords
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getSubPackagePendingList())) {
            List<Long> subPackageDrugIds = curVisitAmountVo.getSubPackagePendingList().stream().map(VisitSubPackageDrug::getId).collect(Collectors.toList());
            paid.setSubPackageDrugIds(JSONArray.toJSONString(subPackageDrugIds));
        }
        // packageRecords
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getPackagePendingList())) {
            List<Long> packageDrugIds = curVisitAmountVo.getPackagePendingList().stream().map(VisitPackageDrug::getId).collect(Collectors.toList());
            paid.setPackageDrugIds(JSONArray.toJSONString(packageDrugIds));
        }
        // inspectionRecords
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getInspectionPendingList())) {
            List<Long> inspectionIds = curVisitAmountVo.getInspectionPendingList().stream().map(VisitInspectionList::getId).collect(Collectors.toList());
            paid.setInspectionIds(JSONArray.toJSONString(inspectionIds));
        }
        paid.setCreatedBy(userName);
        paid.setCreatedTime(LocalDateTime.now());
        paid.setUpdatedBy(userName);
        paid.setUpdatedTime(LocalDateTime.now());
        paidService.save(paid);
    }

    private void updateGuidanceStatus(CurVisitAmountVo curVisitAmountVo, String userName) {
        Guidance guidance = curVisitAmountVo.getGuidance();
        List<VisitInspectionList> inspectionList = inspectionListService.list(new LambdaQueryWrapper<VisitInspectionList>().eq(VisitInspectionList::getGuidanceCode, guidance.getGuidanceCode()));
        visitInspectionListService.updateGuidanceStatus(guidance, inspectionList, userName);
    }

    private void updateDrugStatus(CurVisitAmountVo curVisitAmountVo, @Valid PaidAddReqDto paidAddReqDto) {
        // card
        PatientCard storeCard = patientCardService.getOne(new LambdaQueryWrapper<PatientCard>().eq(PatientCard::getPatientId, curVisitAmountVo.getGuidance().getPatientId()).eq(PatientCard::getCardType, CardTypeEnum.STORED_CARD.getCode()).eq(PatientCard::getStatus, PatientCardStatusEnum.VALID.getCode()));
        BigDecimal storedCardPayableAmount = paidAddReqDto.getDrugPayInfo().getStoredCardAmount();
        // 没有卡，但是输入了扣卡得钱
        if (Objects.isNull(storeCard) && Objects.nonNull(storedCardPayableAmount) && storedCardPayableAmount.compareTo(BigDecimal.ZERO) != 0) {
            throw new BusinessException(ResponseCodeEnum.PAID_AMOUNT_ERROR.getMsg());
        }
        if (storeCard != null) {
            storeCard.setBalance(storeCard.getBalance().subtract(storedCardPayableAmount));
            patientCardService.updateById(storeCard);
        }
        // 分装药品
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getSubPackagePendingList())) {
            List<VisitSubPackageDrug> subPackageDrugList = curVisitAmountVo.getSubPackagePendingList();
            for (VisitSubPackageDrug visitSubPackageDrug : subPackageDrugList) {
                visitSubPackageDrug.setStatus(DrugVisitStatusEnum.PAID.getCode());
                visitSubPackageDrug.setPaidTime(LocalDateTime.now());
            }
            curVisitAmountVo.getGuidance().setDrugTime(LocalDateTime.now());
            visitSubPackageDrugService.updateBatchById(subPackageDrugList);
        }
        // 整装药品
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getPackagePendingList())) {
            List<VisitPackageDrug> packagePendingList = curVisitAmountVo.getPackagePendingList();
            for (VisitPackageDrug visitPackageDrug : packagePendingList) {
                visitPackageDrug.setStatus(DrugVisitStatusEnum.PAID.getCode());
                visitPackageDrug.setPaidTime(LocalDateTime.now());
            }
            curVisitAmountVo.getGuidance().setDrugTime(LocalDateTime.now());
            visitPackageDrugService.updateBatchById(packagePendingList);
        }
        // 实验室检查
        if (!CollectionUtils.isEmpty(curVisitAmountVo.getInspectionPendingList())) {
            List<VisitInspectionList> inspectionPendingList = curVisitAmountVo.getInspectionPendingList();
            for (VisitInspectionList visitInspectionList : inspectionPendingList) {
                visitInspectionList.setStatus(DrugVisitStatusEnum.PAID.getCode());
                visitInspectionList.setPaidTime(LocalDateTime.now());
            }
            if (Objects.isNull(curVisitAmountVo.getGuidance().getInspectionTime())) {
                curVisitAmountVo.getGuidance().setInspectionTime(LocalDateTime.now());
            }
            visitInspectionListService.updateBatchById(inspectionPendingList);
        }
    }

    private CurVisitAmountVo checkPayAmount(PaidAddReqDto paidAddReqDto) {
        // 当前实际需要支付金额
        ApiResult<CurVisitAmountVo> curVisitAmount = this.getCurVisitAmount(paidAddReqDto.getGuidanceCode(), paidAddReqDto.getPatientId());
        BigDecimal cardAmount = curVisitAmount.getData().getCardAmount();
        BigDecimal inspectionAmount = curVisitAmount.getData().getInspectionAmount();
        BigDecimal subPackageDrugAmount = curVisitAmount.getData().getSubPackageDrugAmount();
        BigDecimal packageDrugAmount = curVisitAmount.getData().getPackageDrugAmount();
        BigDecimal limitCardFreeAmount = curVisitAmount.getData().getLimitCardFreeAmount();
        BigDecimal payableTotalAmount = cardAmount.add(inspectionAmount).add(packageDrugAmount).add(subPackageDrugAmount);
        // 请求金额
        BigDecimal reqCardAmount = paidAddReqDto.getPayableAmountInfo().getPayableCardAmount();
        BigDecimal reqDrugAmount = paidAddReqDto.getPayableAmountInfo().getPayableDrugAmount();
        BigDecimal reqPayableTotalAmount = reqCardAmount.add(reqDrugAmount);
        if (payableTotalAmount.compareTo(BigDecimal.ZERO) == 0) {
            throw new BusinessException(ResponseCodeEnum.INTERNAL_SERVER_ERROR.getMsg());
        }
        if (payableTotalAmount.compareTo(reqPayableTotalAmount) != 0) {
            log.error("支付金额有变动");
            log.error("subPackageAmount:{}, packageAmount:{}, inspectionAmount:{}, cardAmount:{}, totalAmount:{}, limitAmount:{}", subPackageDrugAmount, packageDrugAmount, inspectionAmount, cardAmount, payableTotalAmount, limitCardFreeAmount);
            log.error("reqCardAmount: {}, reqDrugAmount:{}, totalmount: {}", reqCardAmount, reqDrugAmount, reqPayableTotalAmount);
            throw new BusinessException(ResponseCodeEnum.AMOUNT_ERROR.getMsg());
        }
        return curVisitAmount.getData();
    }


}




